//== GAIA ============================================================================

//== FILEDOC =========================================================================

/** @file mklex.h
  * @brief Make a lexer
  */

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/lexer/lexcore.h"
#include "sagittarius/lexer/lexx.h"
#include "sagittarius/io/io.h"

#include <stdio.h>
#include <string.h>
#include <malloc.h>

#define DFA_ACC_NONE 0xffffffff

typedef struct {
    constLexBuf w; /* Position in the input buf */
    uint32 s; /* State */
    uint32 ef; /* Error flag */
    uint32 ac; /* Accepted pattern */
} sm_c_parserState;

void init_symbols();
void clear_symbols();
void dfa_init_ps(sm_c_parserState* ps, constLexBuf buf);
void resume_dfa(sm_c_parserState* ps);
void dfa_print_acc_id(uint32 ac);

///CompareArg
//Compares the two strings
//Will fail if lengths do not match
int CompareArg(const char* arg, const char* cmp)
{
    while(*arg == *cmp && *arg != '\0' && *cmp != '\0')
    {
        ++arg;
        ++cmp;
    }
    if(*arg == *cmp)
        return 1;
    else
        return 0;
}
///CompareArg_Short
//Compares two strings until null char is found
//Terminates at null char, returning 1 if strings match
//up to first occurrence of null char
int CompareArg_Short(const char* arg, const char* cmp)
{
    while(1) {
        if(*arg == '\0' || *cmp == '\0')
            return 1;
        if(*arg != *cmp)
            break;
        ++arg;
        ++cmp;
    }
    return 0;
}

int IsUIntegerString(const char* arg)
{
    //ASCII:
    //0x30 = '0'
    //0x39 = '9'
    while(0x30 <= *arg && *arg <= 0x39) {
        ++arg;
    }
    if(*arg == '\0')
        return 1;
    else
        return 0;
}

unsigned int String2UInt(const char* arg)
{
    unsigned int result=0;
    //ASCII:
    //0x30 = '0'
    //0x39 = '9'
    while(0x30 <= *arg && *arg <= 0x39)
    {
        result *= 10;
        result += (unsigned int)(*arg-0x30);
        ++arg;
    }
    assert(*arg == '\0');
    return result;
}

//int becuase getc returns int
int buf_pushc(char** buf, size_t* n, size_t* size, int c) {
    char *tmpbuf;
    if(!(*buf)) {
        *size = 256;
        *buf = malloc(*size);
        *n = 1;
        (*buf)[0] = c;
    } else {
        if(n == size) {
            tmpbuf = malloc(*size << 2);
            memcpy(tmpbuf, *buf, *size);
            *size <<= 2;
            free(*buf);
            *buf = tmpbuf;
        }
        (*buf)[(*n)++] = c;
    }
    return c;
}

int main(int argc, char* argv[]) {
    lexWork* lw;
    int result;//, i;
    char* buf;
    size_t bytes_read;
    int chr;
    LexBuf ubuf;
    
    size_t size=0, n;
    FILE* file=NULL;
    
    /*for(i=1; i<argc-1; ++i)
    {
        //not recognized
        fprintf(stderr, "Error: unrecognized token on command line: %s\n", argv[i]);
        return -1;
    }*/
    
    /*open*/
    if(argc >= 2) {
        fprintf(stderr, "Specified file is %s\n", argv[argc-1]);
        file = fopen(argv[argc-1], "rb");
        if(!file)
        {
            assert(0 && "Failed to open file");
            return -1;
        }
        //get t3h s!z3
        fseek(file, 0, SEEK_END);
        size = ftell(file);
        rewind(file);
        fprintf(stderr, "File size is %lu\n", size);
        assert(size > 0);
        
        //allocated buffer
        assert(sizeof(char) == 1 && "char must be one byte wide");
        buf=(char*)malloc(size+1); //one extra byte for null char
        assert(buf && "Failed to allocate buffer");
        //read the whole file at once
        bytes_read = fread(buf, 1, size, file);
        assert(bytes_read == size && "Failed to read whole file (wrong size specified?)");
        //trip EOF indicator
        fgetc(file);
        assert(feof(file) && "EOF Expected");
        buf[size] = '\0'; //terminating null char
        
        /*close*/
        fclose(file);
    } else {
        fprintf(stderr, "Using standard input\n");
        buf = NULL;
        while(1) {
            chr = getc(stdin);
            if(chr == EOF)
                break;
            buf_pushc(&buf, &n, &size, chr);
        }
        buf_pushc(&buf, &n, &size, '\0');
    }
    
    //create buffer for unicode values
    #if !SAGITTARIUS_LXALPHA_USE_GMP
        ubuf = malloc((size+1)*sizeof(UnicodeCChar)); //size+1 is important for storing the null-terminating value
        assert(ubuf && "Unable to allocate memory");
    #else
        ubuf = sg_bignum_create_buf(size+1);
        assert(ubuf && "Unable to allocate memory");
    #endif
    
    //convert
    #if !SAGITTARIUS_LXALPHA_USE_GMP
        result = UTF8_ConvertBuffer(buf, ubuf);
    #else
        result = UTF8_GMPConvertBuffer(buf, ubuf);
    #endif
    if(result < 0)
    {
        fprintf(stderr, "Failed to convert buffer, routine returned with code %d\n", result);
        free(ubuf);
        return -1;
    }
    free(buf);
    
    /*fprintf(stderr, "Unicode buffer:\n");
    fflush(stdout);
    LX_WRITE_BUF(2, ubuf);
    fprintf(stderr, "\n:End unicode buffer\n\n");*/
    
#if LX_USE_POOLALLOC
    lx_initsegctx(&lx_myseg);
    lx_initsegctx(&lx_tmpseg);
    lx_segstack_push(&lx_myseg);
#endif
    lw = golex(ubuf);
    //dump_radix_node(1, lw->tab.r, 0);
    finalize_lex(lw);
    free(lw);
#if LX_USE_POOLALLOC
    lx_segstack_pop();
    lx_destroysegctx(&lx_myseg);
    lx_destroysegctx(&lx_tmpseg);
#endif
    
    fprintf(stderr, "Generation complete\n");
    
    //free buffer
    #if !SAGITTARIUS_LXALPHA_USE_GMP
        free(ubuf);
    #else
        LX_FREE_BUFFER(ubuf);
    #endif
    
    return 0;
}